import { isDate } from './util'

/* eslint-disable */
class Dayjs {
    utc: boolean
    date: Date
    timeZone: number
    timeZoneString: any
    mYear: any
    mMonth: any
    mDay: any
    mWeek: any
    mHour: any
    mMinute: any
    mSecond: any
    constructor(dateStr?: string | number | Date) {
        this.utc = false
        const parsedDate = this.parseConfig(dateStr)
        this.date = new Date(parsedDate)
        this.timeZone = this.date.getTimezoneOffset() / 60
        this.timeZoneString = this.padNumber(
            String(-1 * this.timeZone).replace(/^(.)?(\d)/, '$10$200'),
            5,
            '+',
        )
        this.mYear = this.date.getFullYear()
        this.mMonth = this.date.getMonth()
        this.mDay = this.date.getDate()
        this.mWeek = this.date.getDay()
        this.mHour = this.date.getHours()
        this.mMinute = this.date.getMinutes()
        this.mSecond = this.date.getSeconds()
    }

    parseConfig(dateStr?: string | number | Date) {
        if (!dateStr) return new Date()
        if (isDate(dateStr)) return dateStr
        if (/^(\d){8}$/.test(dateStr as string)) {
            this.utc = true
            return `${(dateStr as string).substr(0, 4)}-${(dateStr as string).substr(4, 2)}-${(dateStr as string).substr(6, 2)}`
        }
        return dateStr
    }

    padNumber(num: string, length: number, padChar: string) {
        return !num || num.length >= length
            ? num
            : `${Array(length + 1 - num.length).join(padChar)}${num}`
    }

    year() {
        return this.mYear
    }

    month() {
        return this.mMonth
    }

    unix() {
        const timeZoneOffset = this.utc ? 60 * this.timeZone * 60 * 1000 : 0
        return Math.floor((this.date.getTime() + timeZoneOffset) / 1000)
    }

    toString() {
        return this.date.toUTCString()
    }

    startOf(unit: string) {
        switch (unit) {
            case 'year':
                return new Dayjs(new Date(this.year(), 0, 1))
            case 'month':
                return new Dayjs(new Date(this.year(), this.month(), 1))
            default:
                return this
        }
    }

    add(amount: number, unit: string) {
        let interval
        switch (unit) {
            case 'm':
            case 'minutes':
                interval = 60
                break
            case 'h':
            case 'hours':
                interval = 60 * 60
                break
            case 'd':
            case 'days':
                interval = 24 * 60 * 60
                break
            case 'w':
            case 'weeks':
                interval = 7 * 24 * 60 * 60
                break
            default:
                interval = 1
        }
        const newUnixTime = this.unix() + amount * interval
        return new Dayjs(1000 * newUnixTime)
    }

    subtract(amount: number, unit: string) {
        return this.add(-1 * amount, unit)
    }

    format(formatStr = 'YYYY-MM-DDTHH:mm:ssZ') {
        const weekdays = [
            'Sunday',
            'Monday',
            'Tuesday',
            'Wednesday',
            'Thursday',
            'Friday',
            'Saturday',
        ]
        return formatStr.replace(
            /Y{2,4}|M{1,2}|D{1,2}|d{1,4}|H{1,2}|m{1,2}|s{1,2}|Z{1,2}/g,
            (match) => {
                switch (match) {
                    case 'YY':
                        return String(this.mYear).slice(-2)
                    case 'YYYY':
                        return String(this.mYear)
                    case 'M':
                        return String(this.mMonth + 1)
                    case 'MM':
                        return this.padNumber(String(this.mMonth + 1), 2, '0')
                    case 'D':
                        return String(this.mDay)
                    case 'DD':
                        return this.padNumber(String(this.mDay), 2, '0')
                    case 'd':
                        return String(this.mWeek)
                    case 'dddd':
                        return weekdays[this.mWeek]
                    case 'H':
                        return String(this.mHour)
                    case 'HH':
                        return this.padNumber(String(this.mHour), 2, '0')
                    case 'm':
                        return String(this.mMinute)
                    case 'mm':
                        return this.padNumber(String(this.mMinute), 2, '0')
                    case 's':
                        return String(this.mSecond)
                    case 'ss':
                        return this.padNumber(String(this.mSecond), 2, '0')
                    case 'Z':
                        return `${this.timeZoneString.slice(0, -2)}:00`
                    case 'ZZ':
                        return this.timeZoneString
                    default:
                        return match
                }
            },
        )
    }
}

export function dayjs(dateStr?: string | number | Date) {
    return new Dayjs(dateStr)
}
